package m.server.web;

import com.alibaba.fastjson.JSONObject;
import m.common.action.Action;
import m.common.action.RequestParam;
import m.server.web.req.FormData;
import m.server.web.req.Header;
import m.server.web.req.HttpMethod;
import m.system.db.TransactionManager;
import m.system.exception.MException;
import m.system.util.*;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ActionRedirect implements ServerResult {
    /**
     * uriMethod  uri方法
     * requestParam 请求参数
     */
    public ActionRedirect(HttpMethod uriMethod, RequestParam requestParam){
        this.uriMethod=uriMethod;
        this.requestParam=requestParam;
        this.json=null;
    }
    /**
     * uriMethod  uri方法
     * requestParam 请求参数
     * json 附加参数
     */
    public ActionRedirect(HttpMethod uriMethod, RequestParam requestParam, JSONObject json){
        this.uriMethod=uriMethod;
        this.requestParam=requestParam;
        this.json=json;
    }
    /** 默认GET
     * requestParam 请求参数
     */
    public ActionRedirect(RequestParam requestParam){
        this(HttpMethod.GET,requestParam);
    }
    /** 默认POST
     * requestParam 请求参数
     * json 附加参数
     */
    public ActionRedirect(RequestParam requestParam, JSONObject json){
        this(HttpMethod.POST,requestParam,json);
    }
    /** 默认POST
     * requestParam 请求参数
     * json 附加参数
     */
    public ActionRedirect(RequestParam requestParam, JSONMessage json){
        this(HttpMethod.POST,requestParam,json.toJSON());
    }
    /**
     * uriMethod  uri方法
     * requestParam 请求参数
     * json 附加参数
     */
    public ActionRedirect(HttpMethod uriMethod, RequestParam requestParam, JSONMessage json){
        this(uriMethod,requestParam,json.toJSON());
    }

    private final HttpMethod uriMethod;
    private final RequestParam requestParam;
    private Action realAction;
    private final JSONObject json;
    private Class<?>[] params;
    protected Object toResult(Request request, Response response) throws Exception {
        if (null != this.json)
            ActionUtil.fillJSONObject(request.getRequestJson(), this.json);
        realAction = request.getRequestJson().toJavaObject(this.requestParam.getAction());
        realAction.setRequest(request);
        realAction.setResponse(response);
        realAction.setAuthorization(request.getHeader(Header.Authorization));
        fillAction4FormData(request.getFormDatas());
        this.params=this.requestParam.getParamList().values().toArray(new Class<?>[]{});
        //执行action的方法-----------------------------------
        realAction.beforeExecute(this.requestParam);//action方法执行之前执行
        Object r;
        try {
            if (this.requestParam.isTransaction()) {
                TransactionManager tm = new TransactionManager();
                try {
                    tm.begin();
                    r = ClassUtil.executeMethod(realAction, this.requestParam.getMethod(), params, toMethodParams(this.requestParam.getParamList(), request.getRequestJson(),request.getFormDatas()));
                    tm.commit();
                    return r;
                } catch (Exception e) {
                    tm.rollback();
                    throw e;
                }
            } else {
                r = ClassUtil.executeMethod(realAction, this.requestParam.getMethod(), params, toMethodParams(this.requestParam.getParamList(), request.getRequestJson(),request.getFormDatas()));
            }
            realAction.afterExecute(this.requestParam,r);//action方法执行之后执行
        }catch (Exception e){
            r=realAction.exceptionExecute(this.requestParam,e,toResultType());//action方法执行报错后执行
        }
        return r;
    }
    private void fillAction4FormData(List<FormData> datas){
        if(null==datas) return;
        datas.stream().map(FormData::getName).distinct().forEach(
            name->{
                try {
                    fillObject4FormData(realAction,name,name,datas);
                } catch (MException ignored) {}
            }
        );

    }
    private void fillObject4FormData(Object obj, String name,String fullName,List<FormData> datas) throws MException {
        int index=name.indexOf(".");
        if(index>0){
            String f1=name.substring(0,index);
            String f2=name.substring(f1.length()+1);
            Object o=ClassUtil.getFieldValue(obj,f1);
            if(null==o){
                o=ClassUtil.newInstance(ClassUtil.getDeclaredField(obj.getClass(),f1).getType());
                ClassUtil.setFieldValue(obj,f1,o);
            }
            fillObject4FormData(o,f2,fullName,datas);
        }else{
            if(null==ClassUtil.getFieldValue(obj,name)) {
                ClassUtil.setFieldValue(obj, name, getFormData(ClassUtil.getDeclaredField(obj.getClass(),name).getType(), fullName, datas));
            }
        }
    }
    private Object[] toMethodParams(Map<String,Class<?>> paramTypeMap, JSONObject requestBody, List<FormData> datas) {
        List<Object> ls=new ArrayList<>();
        for(String key : paramTypeMap.keySet()) {
            Class<?> clazz=paramTypeMap.get(key);
            if(null==clazz) continue;
            Object obj=null;
            if(requestBody != null) obj=requestBody.get(key);
            if(null==obj) obj=getFormData(clazz,key,datas);
            if(!clazz.equals(String.class)) {
                if(null!=obj&& StringUtil.isSpace(obj.toString())) {
                    obj=null;
                }
            }
            ls.add(ObjectUtil.convert(clazz, obj));
        }
        return ls.toArray(new Object[] {});
    }
    private Object getFormData(Class<?> type, String fullName,List<FormData> datas){
        List<FormData> ls=new ArrayList<>();
        if(null!=datas&&!datas.isEmpty()) {
            for (FormData fd : datas) {
                if (fd.getName().equals(fullName)) ls.add(fd);
            }
        }
        if(!ls.isEmpty()) {
            if (type.isArray()){
                Class<?> et = type.getComponentType();
                Object[] arr=(Object[])Array.newInstance(et, ls.size());
                for(int i=0,len=ls.size();i<len;i++) {
                    arr[i]=convertParam(et,ls.get(i));
                }
                return arr;
            }else{
                return convertParam(type,ls.get(0));
            }
        }else {
            return null;
        }
    }
    private Object convertParam(Class<?> type,FormData fd){
        if(File.class.equals(type)){
            return fd.getFile();
        }else if(FormData.class.equals(type)){
            return fd;
        }else{
            return ObjectUtil.convert(type,fd.getString());
        }
    }
    protected Type toResultType(){
        Method m=ClassUtil.getDeclaredMethods(this.requestParam.getAction(),this.requestParam.getMethod(),this.params);
        if(null!=m) {
            return m.getAnnotatedReturnType().getType();
        }
        return null;
    }
    protected Action getRealAction(){
        return realAction;
    }

    @Override
    public String toString() {
        return this.requestParam.getAction().getName()+"."+this.requestParam.getMethod();
    }
    private ServerResultStatus status=ServerResultStatus.SUCCESS;
    public ActionRedirect setStatus(ServerResultStatus status) {
        this.status = status;
        return this;
    }

    @Override
    public ServerResultStatus getStatus() {
        return this.status;
    }

    @Override
    public String getContent() {
        return this.requestParam.getAction().getName()+"."+this.requestParam.getMethod();
    }
}
